package edu.pku.sei.transformation.editor.parser.texttemplate.equivalence;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import edu.pku.sei.transformation.editor.parser.texttemplate.element.ElementEdge;
import edu.pku.sei.transformation.editor.parser.texttemplate.element.StructuredTextElement;
import edu.pku.sei.transformation.editor.parser.texttemplate.element.TextElement;
import edu.pku.sei.transformation.editor.parser.texttemplate.element.TextElementRegister;

public class EquivalenceNode extends EquivalenceElement<TextElement> {
	public Set<EquivalenceLink> incomings = new HashSet<EquivalenceLink>();
	public Set<EquivalenceLink> outgoings = new HashSet<EquivalenceLink>();
	public String id = "";
	public Class elementType;
	
	public EquivalenceNode(TextElement element) {
		this.elements.add(element);
		this.id = element.getName();
		this.elementType = element.getClass();
		element.setEquivalenceNode(this);
		
		EquivalenceLink last = null;
		if (element instanceof StructuredTextElement) {
			for (ElementEdge edge : ((StructuredTextElement)element).getEdges()) {
				EquivalenceLink link = new EquivalenceLink();
				link.elements.add(edge);
				
				this.addOutgoingLink(link);
				
				EquivalenceNode target = new EquivalenceNode(edge.getTargetTextElement());
				target.addIncomingLink(link);
				
				if (last != null)
					last.addRightLink(link);
				last = link;
			}			
		}
	}
	
	public EquivalenceNode(TextElementRegister register) {
		this(register.getRoot());
	}
	
	@Override
	public boolean merge(EquivalenceElement otherNode) {
		if (otherNode instanceof EquivalenceNode && this.elementType.equals(((EquivalenceNode)otherNode).elementType)) {
			for (Iterator<EquivalenceLink> node1outgoings = this.outgoings.iterator(); node1outgoings.hasNext();) {
				EquivalenceLink link1 = node1outgoings.next();
				EquivalenceNode subnode1 = link1.target;
				for (Iterator<EquivalenceLink> node2outgoings = ((EquivalenceNode)otherNode).outgoings.iterator(); node2outgoings.hasNext();) {
					EquivalenceLink	link2 =  node2outgoings.next();
					EquivalenceNode subnode2 = link2.target;

					if (subnode1.elementType.equals(subnode2.elementType)
							&& subnode1.id.equals(subnode2.id)) {
						link1.merge(link2);
						
						subnode2.incomings.remove(subnode2);
						node2outgoings.remove();	
						
						subnode1.merge(subnode2);
					}
				}
			}
			
			otherNode.isVisited = true;
			
			for (EquivalenceLink incomingLInk : ((EquivalenceNode) otherNode).incomings) {
				addIncomingLink(incomingLInk);
			}
			
			for (EquivalenceLink outgoingLink : ((EquivalenceNode) otherNode).outgoings) {
				addOutgoingLink(outgoingLink);
			}
			
			for (TextElement element : ((EquivalenceNode)otherNode).elements) {
				element.setEquivalenceNode(this);
				this.elements.add(element);
			}
			return true;
		} else {
			return false;
		}
	}
	
	public boolean addIncomingLink(EquivalenceLink link) {
		if (this.incomings.add(link)) {
			link.target = this;
			return true;
		} else {
			return false;
		}
	}
	
	public boolean addOutgoingLink(EquivalenceLink link) {
		if (this.outgoings.add(link)) {
			link.source = this;
			return true;
		} else {
			return false;
		}
	}
	
	@Override
	public String toString() {
		String s = "<node";
		TextElement element = elements.iterator().next();
		if (element instanceof StructuredTextElement) {
			s += "name="+element.getName() + ">\n";
			for (EquivalenceLink link : outgoings) {
				s += link.toString();
			}
		}
		return s+"</node>\n";
	}

}